/** @preserve
jSignature v2 "${buildDate}" "${commitID}"
Copyright (c) 2012 Willow Systems Corp http://willow-systems.com
Copyright (c) 2010 Brinley Ang http://www.unbolt.net
MIT License <http://www.opensource.org/licenses/mit-license.php>

*/
;(function () {
  var apinamespace = 'jSignature'

  /**
Allows one to delay certain eventual action by setting up a timer for it and allowing one to delay it
by "kick"ing it. Sorta like "kick the can down the road"

@public
@class
@param
@returns {Type}
*/
  var KickTimerClass = function (time, callback) {
    var timer
    this.kick = function () {
      clearTimeout(timer)
      timer = setTimeout(callback, time)
    }
    this.clear = function () {
      clearTimeout(timer)
    }
    return this
  }

  var PubSubClass = function (context) {
    'use strict'
    /*  @preserve
	-----------------------------------------------------------------------------------------------
	JavaScript PubSub library
	2012 (c) Willow Systems Corp (www.willow-systems.com)
	based on Peter Higgins (dante@dojotoolkit.org)
	Loosely based on Dojo publish/subscribe API, limited in scope. Rewritten blindly.
	Original is (c) Dojo Foundation 2004-2010. Released under either AFL or new BSD, see:
	http://dojofoundation.org/license for more information.
	-----------------------------------------------------------------------------------------------
	*/
    this.topics = {}
    // here we choose what will be "this" for the called events.
    // if context is defined, it's context. Else, 'this' is this instance of PubSub
    this.context = context ? context : this
    /**
     * Allows caller to emit an event and pass arguments to event listeners.
     * @public
     * @function
     * @param topic {String} Name of the channel on which to voice this event
     * @param **arguments Any number of arguments you want to pass to the listeners of this event.
     */
    this.publish = function (topic, arg1, arg2, etc) {
      'use strict'
      if (this.topics[topic]) {
        var currentTopic = this.topics[topic],
          args = Array.prototype.slice.call(arguments, 1),
          toremove = [],
          torun = [],
          fn,
          i,
          l,
          pair

        for (i = 0, l = currentTopic.length; i < l; i++) {
          pair = currentTopic[i] // this is a [function, once_flag] array
          fn = pair[0]
          if (pair[1] /* 'run once' flag set */) {
            pair[0] = function () {}
            toremove.push(i)
          }
          /* don't call the callback right now, it might decide to add or
           * remove subscribers which will wreak havoc on our index-based
           * iteration */
          torun.push(fn)
        }
        for (i = 0, l = toremove.length; i < l; i++) {
          currentTopic.splice(toremove[i], 1)
        }
        for (i = 0, l = torun.length; i < l; i++) {
          torun[i].apply(this.context, args)
        }
      }
    }
    /**
     * Allows listener code to subscribe to channel and be called when data is available
     * @public
     * @function
     * @param topic {String} Name of the channel on which to voice this event
     * @param callback {Function} Executable (function pointer) that will be ran when event is voiced on this channel.
     * @param once {Boolean} (optional. False by default) Flag indicating if the function is to be triggered only once.
     * @returns {Object} A token object that cen be used for unsubscribing.
     */
    this.subscribe = function (topic, callback, once) {
      'use strict'
      if (!this.topics[topic]) {
        this.topics[topic] = [[callback, once]]
      } else {
        this.topics[topic].push([callback, once])
      }
      return {
        'topic': topic,
        'callback': callback
      }
    }
    /**
     * Allows listener code to unsubscribe from a channel
     * @public
     * @function
     * @param token {Object} A token object that was returned by `subscribe` method
     */
    this.unsubscribe = function (token) {
      if (this.topics[token.topic]) {
        var currentTopic = this.topics[token.topic]

        for (var i = 0, l = currentTopic.length; i < l; i++) {
          if (currentTopic[i] && currentTopic[i][0] === token.callback) {
            currentTopic.splice(i, 1)
          }
        }
      }
    }
  }

  /// Returns front, back and "decor" colors derived from element (as jQuery obj)
  function getColors($e) {
    var tmp,
      undef,
      frontcolor = $e.css('color'),
      backcolor,
      e = $e[0]

    var toOfDOM = false
    while (e && !backcolor && !toOfDOM) {
      try {
        tmp = $(e).css('background-color')
      } catch (ex) {
        tmp = 'transparent'
      }
      if (tmp !== 'transparent' && tmp !== 'rgba(0, 0, 0, 0)') {
        backcolor = tmp
      }
      toOfDOM = e.body
      e = e.parentNode
    }

    var rgbaregex = /rgb[a]*\((\d+),\s*(\d+),\s*(\d+)/, // modern browsers
      hexregex = /#([AaBbCcDdEeFf\d]{2})([AaBbCcDdEeFf\d]{2})([AaBbCcDdEeFf\d]{2})/, // IE 8 and less.
      frontcolorcomponents

    // Decomposing Front color into R, G, B ints
    tmp = undef
    tmp = frontcolor.match(rgbaregex)
    if (tmp) {
      frontcolorcomponents = { 'r': parseInt(tmp[1], 10), 'g': parseInt(tmp[2], 10), 'b': parseInt(tmp[3], 10) }
    } else {
      tmp = frontcolor.match(hexregex)
      if (tmp) {
        frontcolorcomponents = { 'r': parseInt(tmp[1], 16), 'g': parseInt(tmp[2], 16), 'b': parseInt(tmp[3], 16) }
      }
    }
    //		if(!frontcolorcomponents){
    //			frontcolorcomponents = {'r':255,'g':255,'b':255}
    //		}

    var backcolorcomponents
    // Decomposing back color into R, G, B ints
    if (!backcolor) {
      // HIghly unlikely since this means that no background styling was applied to any element from here to top of dom.
      // we'll pick up back color from front color
      if (frontcolorcomponents) {
        if (Math.max.apply(null, [frontcolorcomponents.r, frontcolorcomponents.g, frontcolorcomponents.b]) > 127) {
          backcolorcomponents = { 'r': 0, 'g': 0, 'b': 0 }
        } else {
          backcolorcomponents = { 'r': 255, 'g': 255, 'b': 255 }
        }
      } else {
        // arg!!! front color is in format we don't understand (hsl, named colors)
        // Let's just go with white background.
        backcolorcomponents = { 'r': 255, 'g': 255, 'b': 255 }
      }
    } else {
      tmp = undef
      tmp = backcolor.match(rgbaregex)
      if (tmp) {
        backcolorcomponents = { 'r': parseInt(tmp[1], 10), 'g': parseInt(tmp[2], 10), 'b': parseInt(tmp[3], 10) }
      } else {
        tmp = backcolor.match(hexregex)
        if (tmp) {
          backcolorcomponents = { 'r': parseInt(tmp[1], 16), 'g': parseInt(tmp[2], 16), 'b': parseInt(tmp[3], 16) }
        }
      }
      //			if(!backcolorcomponents){
      //				backcolorcomponents = {'r':0,'g':0,'b':0}
      //			}
    }

    // Deriving Decor color
    // THis is LAZY!!!! Better way would be to use HSL and adjust luminocity. However, that could be an overkill.

    var toRGBfn = function (o) {
        return 'rgb(' + [o.r, o.g, o.b].join(', ') + ')'
      },
      decorcolorcomponents,
      frontcolorbrightness,
      adjusted

    if (frontcolorcomponents && backcolorcomponents) {
      var backcolorbrightness = Math.max.apply(null, [
        frontcolorcomponents.r,
        frontcolorcomponents.g,
        frontcolorcomponents.b
      ])

      frontcolorbrightness = Math.max.apply(null, [backcolorcomponents.r, backcolorcomponents.g, backcolorcomponents.b])
      adjusted = Math.round(frontcolorbrightness + -1 * (frontcolorbrightness - backcolorbrightness) * 0.75) // "dimming" the difference between pen and back.
      decorcolorcomponents = { 'r': adjusted, 'g': adjusted, 'b': adjusted } // always shade of gray
    } else if (frontcolorcomponents) {
      frontcolorbrightness = Math.max.apply(null, [
        frontcolorcomponents.r,
        frontcolorcomponents.g,
        frontcolorcomponents.b
      ])
      var polarity = +1
      if (frontcolorbrightness > 127) {
        polarity = -1
      }
      // shifting by 25% (64 points on RGB scale)
      adjusted = Math.round(frontcolorbrightness + polarity * 96) // "dimming" the pen's color by 75% to get decor color.
      decorcolorcomponents = { 'r': adjusted, 'g': adjusted, 'b': adjusted } // always shade of gray
    } else {
      decorcolorcomponents = { 'r': 191, 'g': 191, 'b': 191 } // always shade of gray
    }

    return {
      'color': frontcolor,
      'background-color': backcolorcomponents ? toRGBfn(backcolorcomponents) : backcolor,
      'decor-color': toRGBfn(decorcolorcomponents)
    }
  }

  function Vector(x, y) {
    this.x = x
    this.y = y
    this.reverse = function () {
      return new this.constructor(this.x * -1, this.y * -1)
    }
    this._length = null
    this.getLength = function () {
      if (!this._length) {
        this._length = Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2))
      }
      return this._length
    }

    var polarity = function (e) {
      return Math.round(e / Math.abs(e))
    }
    this.resizeTo = function (length) {
      // proportionally changes x,y such that the hypotenuse (vector length) is = new length
      if (this.x === 0 && this.y === 0) {
        this._length = 0
      } else if (this.x === 0) {
        this._length = length
        this.y = length * polarity(this.y)
      } else if (this.y === 0) {
        this._length = length
        this.x = length * polarity(this.x)
      } else {
        var proportion = Math.abs(this.y / this.x),
          x = Math.sqrt(Math.pow(length, 2) / (1 + Math.pow(proportion, 2))),
          y = proportion * x
        this._length = length
        this.x = x * polarity(this.x)
        this.y = y * polarity(this.y)
      }
      return this
    }

    /**
     * Calculates the angle between 'this' vector and another.
     * @public
     * @function
     * @returns {Number} The angle between the two vectors as measured in PI.
     */
    this.angleTo = function (vectorB) {
      var divisor = this.getLength() * vectorB.getLength()
      if (divisor === 0) {
        return 0
      } else {
        // JavaScript floating point math is screwed up.
        // because of it, the core of the formula can, on occasion, have values
        // over 1.0 and below -1.0.
        return Math.acos(Math.min(Math.max((this.x * vectorB.x + this.y * vectorB.y) / divisor, -1.0), 1.0)) / Math.PI
      }
    }
  }

  function Point(x, y) {
    this.x = x
    this.y = y

    this.getVectorToCoordinates = function (x, y) {
      return new Vector(x - this.x, y - this.y)
    }
    this.getVectorFromCoordinates = function (x, y) {
      return this.getVectorToCoordinates(x, y).reverse()
    }
    this.getVectorToPoint = function (point) {
      return new Vector(point.x - this.x, point.y - this.y)
    }
    this.getVectorFromPoint = function (point) {
      return this.getVectorToPoint(point).reverse()
    }
  }

  /*
   * About data structure:
   * We don't store / deal with "pictures" this signature capture code captures "vectors"
   *
   * We don't store bitmaps. We store "strokes" as arrays of arrays. (Actually, arrays of objects containing arrays of coordinates.
   *
   * Stroke = mousedown + mousemoved * n (+ mouseup but we don't record that as that was the "end / lack of movement" indicator)
   *
   * Vectors = not classical vectors where numbers indicated shift relative last position. Our vectors are actually coordinates against top left of canvas.
   * 			we could calc the classical vectors, but keeping the the actual coordinates allows us (through Math.max / min)
   * 			to calc the size of resulting drawing very quickly. If we want classical vectors later, we can always get them in backend code.
   *
   * So, the data structure:
   *
   * var data = [
   * 	{ // stroke starts
   * 		x : [101, 98, 57, 43] // x points
   * 		, y : [1, 23, 65, 87] // y points
   * 	} // stroke ends
   * 	, { // stroke starts
   * 		x : [55, 56, 57, 58] // x points
   * 		, y : [101, 97, 54, 4] // y points
   * 	} // stroke ends
   * 	, { // stroke consisting of just a dot
   * 		x : [53] // x points
   * 		, y : [151] // y points
   * 	} // stroke ends
   * ]
   *
   * we don't care or store stroke width (it's canvas-size-relative), color, shadow values. These can be added / changed on whim post-capture.
   *
   */
  function DataEngine(storageObject, context, startStrokeFn, addToStrokeFn, endStrokeFn) {
    this.data = storageObject // we expect this to be an instance of Array
    this.context = context

    if (storageObject.length) {
      // we have data to render
      var numofstrokes = storageObject.length,
        stroke,
        numofpoints

      for (var i = 0; i < numofstrokes; i++) {
        stroke = storageObject[i]
        numofpoints = stroke.x.length
        startStrokeFn.call(context, stroke)
        for (var j = 1; j < numofpoints; j++) {
          addToStrokeFn.call(context, stroke, j)
        }
        endStrokeFn.call(context, stroke)
      }
    }

    this.changed = function () {}

    this.startStrokeFn = startStrokeFn
    this.addToStrokeFn = addToStrokeFn
    this.endStrokeFn = endStrokeFn

    this.inStroke = false

    this._lastPoint = null
    this._stroke = null
    this.startStroke = function (point) {
      if (point && typeof point.x == 'number' && typeof point.y == 'number') {
        this._stroke = { 'x': [point.x], 'y': [point.y] }
        this.data.push(this._stroke)
        this._lastPoint = point
        this.inStroke = true
        // 'this' does not work same inside setTimeout(
        var stroke = this._stroke,
          fn = this.startStrokeFn,
          context = this.context
        setTimeout(
          // some IE's don't support passing args per setTimeout API. Have to create closure every time instead.
          function () {
            fn.call(context, stroke)
          },
          3
        )
        return point
      } else {
        return null
      }
    }
    // that "5" at the very end of this if is important to explain.
    // we do NOT render links between two captured points (in the middle of the stroke) if the distance is shorter than that number.
    // not only do we NOT render it, we also do NOT capture (add) these intermediate points to storage.
    // when clustering of these is too tight, it produces noise on the line, which, because of smoothing, makes lines too curvy.
    // maybe, later, we can expose this as a configurable setting of some sort.
    this.addToStroke = function (point) {
      if (
        this.inStroke &&
        typeof point.x === 'number' &&
        typeof point.y === 'number' &&
        // calculates absolute shift in diagonal pixels away from original point
        Math.abs(point.x - this._lastPoint.x) + Math.abs(point.y - this._lastPoint.y) > 4
      ) {
        var positionInStroke = this._stroke.x.length
        this._stroke.x.push(point.x)
        this._stroke.y.push(point.y)
        this._lastPoint = point

        var stroke = this._stroke,
          fn = this.addToStrokeFn,
          context = this.context
        setTimeout(
          // some IE's don't support passing args per setTimeout API. Have to create closure every time instead.
          function () {
            fn.call(context, stroke, positionInStroke)
          },
          3
        )
        return point
      } else {
        return null
      }
    }
    this.endStroke = function () {
      var c = this.inStroke
      this.inStroke = false
      this._lastPoint = null
      if (c) {
        var stroke = this._stroke,
          fn = this.endStrokeFn, // 'this' does not work same inside setTimeout(
          context = this.context,
          changedfn = this.changed
        setTimeout(
          // some IE's don't support passing args per setTimeout API. Have to create closure every time instead.
          function () {
            fn.call(context, stroke)
            changedfn.call(context)
          },
          3
        )
        return true
      } else {
        return null
      }
    }
  }

  var basicDot = function (ctx, x, y, size) {
      var fillStyle = ctx.fillStyle
      ctx.fillStyle = ctx.strokeStyle
      ctx.fillRect(x + size / -2, y + size / -2, size, size)
      ctx.fillStyle = fillStyle
    },
    basicLine = function (ctx, startx, starty, endx, endy) {
      ctx.beginPath()
      ctx.moveTo(startx, starty)
      ctx.lineTo(endx, endy)
      ctx.closePath()
      ctx.stroke()
    },
    basicCurve = function (ctx, startx, starty, endx, endy, cp1x, cp1y, cp2x, cp2y) {
      ctx.beginPath()
      ctx.moveTo(startx, starty)
      ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, endx, endy)
      ctx.closePath()
      ctx.stroke()
    },
    strokeStartCallback = function (stroke) {
      // this = jSignatureClass instance
      basicDot(this.canvasContext, stroke.x[0], stroke.y[0], this.settings.lineWidth)
    },
    strokeAddCallback = function (stroke, positionInStroke) {
      // this = jSignatureClass instance

      // Because we are funky this way, here we draw TWO curves.
      // 1. POSSIBLY "this line" - spanning from point right before us, to this latest point.
      // 2. POSSIBLY "prior curve" - spanning from "latest point" to the one before it.

      // Why you ask?
      // long lines (ones with many pixels between them) do not look good when they are part of a large curvy stroke.
      // You know, the jaggedy crocodile spine instead of a pretty, smooth curve. Yuck!
      // We want to approximate pretty curves in-place of those ugly lines.
      // To approximate a very nice curve we need to know the direction of line before and after.
      // Hence, on long lines we actually wait for another point beyond it to come back from
      // mousemoved before we draw this curve.

      // So for "prior curve" to be calc'ed we need 4 points
      // 	A, B, C, D (we are on D now, A is 3 points in the past.)
      // and 3 lines:
      //  pre-line (from points A to B),
      //  this line (from points B to C), (we call it "this" because if it was not yet, it's the only one we can draw for sure.)
      //  post-line (from points C to D) (even through D point is 'current' we don't know how we can draw it yet)
      //
      // Well, actually, we don't need to *know* the point A, just the vector A->B
      var Cpoint = new Point(stroke.x[positionInStroke - 1], stroke.y[positionInStroke - 1]),
        Dpoint = new Point(stroke.x[positionInStroke], stroke.y[positionInStroke]),
        CDvector = Cpoint.getVectorToPoint(Dpoint)

      // Again, we have a chance here to draw TWO things:
      //  BC Curve (only if it's long, because if it was short, it was drawn by previous callback) and
      //  CD Line (only if it's short)

      // So, let's start with BC curve.
      // if there is only 2 points in stroke array, we don't have "history" long enough to have point B, let alone point A.
      // Falling through to drawing line CD is proper, as that's the only line we have points for.
      if (positionInStroke > 1) {
        // we are here when there are at least 3 points in stroke array.
        var Bpoint = new Point(stroke.x[positionInStroke - 2], stroke.y[positionInStroke - 2]),
          BCvector = Bpoint.getVectorToPoint(Cpoint),
          ABvector
        if (BCvector.getLength() > this.lineCurveThreshold) {
          // Yey! Pretty curves, here we come!
          if (positionInStroke > 2) {
            // we are here when at least 4 points in stroke array.
            ABvector = new Point(stroke.x[positionInStroke - 3], stroke.y[positionInStroke - 3]).getVectorToPoint(
              Bpoint
            )
          } else {
            ABvector = new Vector(0, 0)
          }

          var minlenfraction = 0.05,
            maxlen = BCvector.getLength() * 0.35,
            ABCangle = BCvector.angleTo(ABvector.reverse()),
            BCDangle = CDvector.angleTo(BCvector.reverse()),
            BCP1vector = new Vector(ABvector.x + BCvector.x, ABvector.y + BCvector.y).resizeTo(
              Math.max(minlenfraction, ABCangle) * maxlen
            ),
            CCP2vector = new Vector(BCvector.x + CDvector.x, BCvector.y + CDvector.y)
              .reverse()
              .resizeTo(Math.max(minlenfraction, BCDangle) * maxlen)

          basicCurve(
            this.canvasContext,
            Bpoint.x,
            Bpoint.y,
            Cpoint.x,
            Cpoint.y,
            Bpoint.x + BCP1vector.x,
            Bpoint.y + BCP1vector.y,
            Cpoint.x + CCP2vector.x,
            Cpoint.y + CCP2vector.y
          )
        }
      }
      if (CDvector.getLength() <= this.lineCurveThreshold) {
        basicLine(this.canvasContext, Cpoint.x, Cpoint.y, Dpoint.x, Dpoint.y)
      }
    },
    strokeEndCallback = function (stroke) {
      // this = jSignatureClass instance

      // Here we tidy up things left unfinished in last strokeAddCallback run.

      // What's POTENTIALLY left unfinished there is the curve between the last points
      // in the stroke, if the len of that line is more than lineCurveThreshold
      // If the last line was shorter than lineCurveThreshold, it was drawn there, and there
      // is nothing for us here to do.
      // We can also be called when there is only one point in the stroke (meaning, the
      // stroke was just a dot), in which case, again, there is nothing for us to do.

      // So for "this curve" to be calc'ed we need 3 points
      // 	A, B, C
      // and 2 lines:
      //  pre-line (from points A to B),
      //  this line (from points B to C)
      // Well, actually, we don't need to *know* the point A, just the vector A->B
      // so, we really need points B, C and AB vector.
      var positionInStroke = stroke.x.length - 1

      if (positionInStroke > 0) {
        // there are at least 2 points in the stroke.we are in business.
        var Cpoint = new Point(stroke.x[positionInStroke], stroke.y[positionInStroke]),
          Bpoint = new Point(stroke.x[positionInStroke - 1], stroke.y[positionInStroke - 1]),
          BCvector = Bpoint.getVectorToPoint(Cpoint),
          ABvector
        if (BCvector.getLength() > this.lineCurveThreshold) {
          // yep. This one was left undrawn in prior callback. Have to draw it now.
          if (positionInStroke > 1) {
            // we have at least 3 elems in stroke
            ABvector = new Point(stroke.x[positionInStroke - 2], stroke.y[positionInStroke - 2]).getVectorToPoint(
              Bpoint
            )
            var BCP1vector = new Vector(ABvector.x + BCvector.x, ABvector.y + BCvector.y).resizeTo(
              BCvector.getLength() / 2
            )
            basicCurve(
              this.canvasContext,
              Bpoint.x,
              Bpoint.y,
              Cpoint.x,
              Cpoint.y,
              Bpoint.x + BCP1vector.x,
              Bpoint.y + BCP1vector.y,
              Cpoint.x,
              Cpoint.y
            )
          } else {
            // Since there is no AB leg, there is no curve to draw. This line is still "long" but no curve.
            basicLine(this.canvasContext, Bpoint.x, Bpoint.y, Cpoint.x, Cpoint.y)
          }
        }
      }
    }

  /*
var getDataStats = function(){
	var strokecnt = strokes.length
		, stroke
		, pointid
		, pointcnt
		, x, y
		, maxX = Number.NEGATIVE_INFINITY
		, maxY = Number.NEGATIVE_INFINITY
		, minX = Number.POSITIVE_INFINITY
		, minY = Number.POSITIVE_INFINITY
	for(strokeid = 0; strokeid < strokecnt; strokeid++){
		stroke = strokes[strokeid]
		pointcnt = stroke.length
		for(pointid = 0; pointid < pointcnt; pointid++){
			x = stroke.x[pointid]
			y = stroke.y[pointid]
			if (x > maxX){
				maxX = x
			} else if (x < minX) {
				minX = x
			}
			if (y > maxY){
				maxY = y
			} else if (y < minY) {
				minY = y
			}
		}
	}
	return {'maxX': maxX, 'minX': minX, 'maxY': maxY, 'minY': minY}
}
*/

  function conditionallyLinkCanvasResizeToWindowResize(jSignatureInstance, settingsWidth, apinamespace, globalEvents) {
    'use strict'
    if (settingsWidth === 'ratio' || settingsWidth.split('')[settingsWidth.length - 1] === '%') {
      this.eventTokens[apinamespace + '.parentresized'] = globalEvents.subscribe(
        apinamespace + '.parentresized',
        (function (eventTokens, $parent, originalParentWidth, sizeRatio) {
          'use strict'

          return function () {
            'use strict'

            var w = $parent.width()
            if (w !== originalParentWidth) {
              // UNsubscribing this particular instance of signature pad only.
              // there is a separate `eventTokens` per each instance of signature pad
              for (var key in eventTokens) {
                if (eventTokens.hasOwnProperty(key)) {
                  globalEvents.unsubscribe(eventTokens[key])
                  delete eventTokens[key]
                }
              }

              var settings = jSignatureInstance.settings
              jSignatureInstance.$parent.children().remove()
              for (var key in jSignatureInstance) {
                if (jSignatureInstance.hasOwnProperty(key)) {
                  delete jSignatureInstance[key]
                }
              }

              // scale data to new signature pad size
              settings.data = (function (data, scale) {
                var newData = []
                var o, i, l, j, m, stroke
                for (i = 0, l = data.length; i < l; i++) {
                  stroke = data[i]

                  o = { 'x': [], 'y': [] }

                  for (j = 0, m = stroke.x.length; j < m; j++) {
                    o.x.push(stroke.x[j] * scale)
                    o.y.push(stroke.y[j] * scale)
                  }

                  newData.push(o)
                }
                return newData
              })(settings.data, (w * 1.0) / originalParentWidth)

              $parent[apinamespace](settings)
            }
          }
        })(this.eventTokens, this.$parent, this.$parent.width(), (this.canvas.width * 1.0) / this.canvas.height)
      )
    }
  }

  function jSignatureClass(parent, options, instanceExtensions) {
    var $parent = (this.$parent = $(parent)),
      eventTokens = (this.eventTokens = {}),
      events = (this.events = new PubSubClass(this)),
      globalEvents = $.fn[apinamespace]('globalEvents'),
      settings = {
        'width': 'ratio',
        'height': 'ratio',
        'sizeRatio': 4, // only used when height = 'ratio'
        'color': '#000',
        'background-color': '#fff',
        'decor-color': '#eee',
        'lineWidth': 0,
        'minFatFingerCompensation': -10,
        'showUndoButton': false,
        'readOnly': false,
        'data': [],
        'signatureLine': false
      }

    $.extend(settings, getColors($parent))
    if (options) {
      $.extend(settings, options)
    }
    this.settings = settings

    for (var extensionName in instanceExtensions) {
      if (instanceExtensions.hasOwnProperty(extensionName)) {
        instanceExtensions[extensionName].call(this, extensionName)
      }
    }

    this.events.publish(apinamespace + '.initializing')

    // these, when enabled, will hover above the sig area. Hence we append them to DOM before canvas.
    this.$controlbarUpper = (function () {
      var controlbarstyle =
        'padding:0 !important; margin:0 !important;' +
        'width: 100% !important; height: 0 !important; -ms-touch-action: none; touch-action: none;' +
        'margin-top:-1em !important; margin-bottom:1em !important;'
      return $('<div style="' + controlbarstyle + '"></div>').appendTo($parent)
    })()

    this.isCanvasEmulator = false // will be flipped by initializer when needed.
    var canvas = (this.canvas = this.initializeCanvas(settings)),
      $canvas = $(canvas)

    this.$controlbarLower = (function () {
      var controlbarstyle =
        'padding:0 !important; margin:0 !important;' +
        'width: 100% !important; height: 0 !important; -ms-touch-action: none; touch-action: none;' +
        'margin-top:-1.5em !important; margin-bottom:1.5em !important; position: relative;'
      return $('<div style="' + controlbarstyle + '"></div>').appendTo($parent)
    })()

    this.canvasContext = canvas.getContext('2d')

    // Most of our exposed API will be looking for this:
    $canvas.data(apinamespace + '.this', this)

    settings.lineWidth = (function (defaultLineWidth, canvasWidth) {
      if (!defaultLineWidth) {
        return Math.max(
          Math.round(canvasWidth / 400) /*+1 pixel for every extra 300px of width.*/,
          2 /* minimum line width */
        )
      } else {
        return defaultLineWidth
      }
    })(settings.lineWidth, canvas.width)

    this.lineCurveThreshold = settings.lineWidth * 3

    // Add custom class if defined
    if (settings.cssclass && $.trim(settings.cssclass) != '') {
      $canvas.addClass(settings.cssclass)
    }

    // used for shifting the drawing point up on touch devices, so one can see the drawing above the finger.
    this.fatFingerCompensation = 0

    var movementHandlers = function (jSignatureInstance) {
      //================================
      // mouse down, move, up handlers:

      // shifts - adjustment values in viewport pixels drived from position of canvas on the page
      var shiftX,
        shiftY,
        setStartValues = function () {
          var tos = $(jSignatureInstance.canvas).offset()
          shiftX = tos.left * -1
          shiftY = tos.top * -1
        },
        getPointFromEvent = function (e) {
          var firstEvent = e.changedTouches && e.changedTouches.length > 0 ? e.changedTouches[0] : e
          // All devices i tried report correct coordinates in pageX,Y
          // Android Chrome 2.3.x, 3.1, 3.2., Opera Mobile,  safari iOS 4.x,
          // Windows: Chrome, FF, IE9, Safari
          // None of that scroll shift calc vs screenXY other sigs do is needed.
          // ... oh, yeah, the "fatFinger.." is for tablets so that people see what they draw.
          return new Point(
            Math.round(firstEvent.pageX + shiftX),
            Math.round(firstEvent.pageY + shiftY) + jSignatureInstance.fatFingerCompensation
          )
        },
        timer = new KickTimerClass(750, function () {
          jSignatureInstance.dataEngine.endStroke()
        })

      this.drawEndHandler = function (e) {
        if (!jSignatureInstance.settings.readOnly) {
          try {
            e.preventDefault()
          } catch (ex) {}
          timer.clear()
          jSignatureInstance.dataEngine.endStroke()
        }
      }
      this.drawStartHandler = function (e) {
        if (!jSignatureInstance.settings.readOnly) {
          e.preventDefault()
          // for performance we cache the offsets
          // we recalc these only at the beginning the stroke
          setStartValues()
          jSignatureInstance.dataEngine.startStroke(getPointFromEvent(e))
          timer.kick()
        }
      }
      this.drawMoveHandler = function (e) {
        if (!jSignatureInstance.settings.readOnly) {
          e.preventDefault()
          if (!jSignatureInstance.dataEngine.inStroke) {
            return
          }
          jSignatureInstance.dataEngine.addToStroke(getPointFromEvent(e))
          timer.kick()
        }
      }

      return this
    }.call({}, this)

    //
    //================================

    ;(function (drawEndHandler, drawStartHandler, drawMoveHandler) {
      var canvas = this.canvas,
        $canvas = $(canvas),
        undef
      if (this.isCanvasEmulator) {
        $canvas.bind('mousemove.' + apinamespace, drawMoveHandler)
        $canvas.bind('mouseup.' + apinamespace, drawEndHandler)
        $canvas.bind('mousedown.' + apinamespace, drawStartHandler)
      } else {
        var hasEventListener = typeof canvas.addEventListener === 'function'
        this.ontouchstart = function (e) {
          canvas.onmousedown = canvas.onmouseup = canvas.onmousemove = undef

          this.fatFingerCompensation =
            settings.minFatFingerCompensation && settings.lineWidth * -3 > settings.minFatFingerCompensation
              ? settings.lineWidth * -3
              : settings.minFatFingerCompensation

          drawStartHandler(e)

          if (hasEventListener) {
            canvas.addEventListener('touchend', drawEndHandler)
            canvas.addEventListener('touchstart', drawStartHandler)
            canvas.addEventListener('touchmove', drawMoveHandler)
          } else {
            canvas.ontouchend = drawEndHandler
            canvas.ontouchstart = drawStartHandler
            canvas.ontouchmove = drawMoveHandler
          }
        }

        if (hasEventListener) {
          canvas.addEventListener('touchstart', this.ontouchstart)
        } else {
          canvas.ontouchstart = ontouchstart
        }

        canvas.onmousedown = function (e) {
          if (hasEventListener) {
            canvas.removeEventListener('touchstart', this.ontouchstart)
          } else {
            canvas.ontouchstart = canvas.ontouchend = canvas.ontouchmove = undef
          }

          drawStartHandler(e)

          canvas.onmousedown = drawStartHandler
          canvas.onmouseup = drawEndHandler
          canvas.onmousemove = drawMoveHandler
        }
        if (window.navigator.msPointerEnabled) {
          canvas.onmspointerdown = drawStartHandler
          canvas.onmspointerup = drawEndHandler
          canvas.onmspointermove = drawMoveHandler
        }
      }
    }).call(this, movementHandlers.drawEndHandler, movementHandlers.drawStartHandler, movementHandlers.drawMoveHandler)

    //=========================================
    // various event handlers

    // on mouseout + mouseup canvas did not know that mouseUP fired. Continued to draw despite mouse UP.
    // it is bettr than
    // $canvas.bind('mouseout', drawEndHandler)
    // because we don't want to break the stroke where user accidentally gets ouside and wants to get back in quickly.
    eventTokens[apinamespace + '.windowmouseup'] = globalEvents.subscribe(
      apinamespace + '.windowmouseup',
      movementHandlers.drawEndHandler
    )

    this.events.publish(apinamespace + '.attachingEventHandlers')

    // If we have proportional width, we sign up to events broadcasting "window resized" and checking if
    // parent's width changed. If so, we (1) extract settings + data from current signature pad,
    // (2) remove signature pad from parent, and (3) reinit new signature pad at new size with same settings, (rescaled) data.
    conditionallyLinkCanvasResizeToWindowResize.call(
      this,
      this,
      settings.width.toString(10),
      apinamespace,
      globalEvents
    )

    // end of event handlers.
    // ===============================

    this.resetCanvas(settings.data)

    // resetCanvas renders the data on the screen and fires ONE "change" event
    // if there is data. If you have controls that rely on "change" firing
    // attach them to something that runs before this.resetCanvas, like
    // apinamespace+'.attachingEventHandlers' that fires a bit higher.
    this.events.publish(apinamespace + '.initialized')

    return this
  } // end of initBase

  //=========================================================================
  // jSignatureClass's methods and supporting fn's

  jSignatureClass.prototype.resetCanvas = function (data, dontClear) {
    var canvas = this.canvas,
      settings = this.settings,
      ctx = this.canvasContext,
      isCanvasEmulator = this.isCanvasEmulator,
      cw = canvas.width,
      ch = canvas.height

    // preparing colors, drawing area
    if (!dontClear) {
      ctx.clearRect(0, 0, cw + 30, ch + 30)
    }

    ctx.shadowColor = ctx.fillStyle = settings['background-color']
    if (isCanvasEmulator) {
      // FLashCanvas fills with Black by default, covering up the parent div's background
      // hence we refill
      ctx.fillRect(0, 0, cw + 30, ch + 30)
    }

    ctx.lineWidth = Math.ceil(parseInt(settings.lineWidth, 10))
    ctx.lineCap = ctx.lineJoin = 'round'

    // signature line
    if (settings.signatureLine) {
      if (null != settings['decor-color']) {
        ctx.strokeStyle = settings['decor-color']
        ctx.shadowOffsetX = 0
        ctx.shadowOffsetY = 0
        var lineoffset = Math.round(ch / 5)
        basicLine(ctx, lineoffset * 1.5, ch - lineoffset, cw - lineoffset * 1.5, ch - lineoffset)
      }

      if (!isCanvasEmulator) {
        ctx.shadowColor = ctx.strokeStyle
        ctx.shadowOffsetX = ctx.lineWidth * 0.5
        ctx.shadowOffsetY = ctx.lineWidth * -0.6
        ctx.shadowBlur = 0
      }
    }

    ctx.strokeStyle = settings.color

    // setting up new dataEngine

    if (!data) {
      data = []
    }

    var dataEngine = (this.dataEngine = new DataEngine(
      data,
      this,
      strokeStartCallback,
      strokeAddCallback,
      strokeEndCallback
    ))

    settings.data = data // onwindowresize handler uses it, i think.
    $(canvas)
      .data(apinamespace + '.data', data)
      .data(apinamespace + '.settings', settings)

    // we fire "change" event on every change in data.
    // setting this up:
    dataEngine.changed = (function (target, events, apinamespace) {
      'use strict'
      return function () {
        events.publish(apinamespace + '.change')
        target.trigger('change')
      }
    })(this.$parent, this.events, apinamespace)
    // let's trigger change on all data reloads
    dataEngine.changed()

    // import filters will be passing this back as indication of "we rendered"
    return true
  }

  function initializeCanvasEmulator(canvas) {
    if (canvas.getContext) {
      return false
    } else {
      // for cases when jSignature, FlashCanvas is inserted
      // from one window into another (child iframe)
      // 'window' and 'FlashCanvas' may be stuck behind
      // in that other parent window.
      // we need to find it
      var window = canvas.ownerDocument.parentWindow
      var FC = window.FlashCanvas
        ? canvas.ownerDocument.parentWindow.FlashCanvas
        : typeof FlashCanvas === 'undefined'
          ? undefined
          : FlashCanvas

      if (FC) {
        canvas = FC.initElement(canvas)

        var zoom = 1
        // FlashCanvas uses flash which has this annoying habit of NOT scaling with page zoom.
        // It matches pixel-to-pixel to screen instead.
        // Since we are targeting ONLY IE 7, 8 with FlashCanvas, we will test the zoom only the IE8, IE7 way
        if (window && window.screen && window.screen.deviceXDPI && window.screen.logicalXDPI) {
          zoom = (window.screen.deviceXDPI * 1.0) / window.screen.logicalXDPI
        }
        if (zoom !== 1) {
          try {
            // We effectively abuse the brokenness of FlashCanvas and force the flash rendering surface to
            // occupy larger pixel dimensions than the wrapping, scaled up DIV and Canvas elems.
            $(canvas)
              .children('object')
              .get(0)
              .resize(Math.ceil(canvas.width * zoom), Math.ceil(canvas.height * zoom))
            // And by applying "scale" transformation we can talk "browser pixels" to FlashCanvas
            // and have it translate the "browser pixels" to "screen pixels"
            canvas.getContext('2d').scale(zoom, zoom)
            // Note to self: don't reuse Canvas element. Repeated "scale" are cumulative.
          } catch (ex) {}
        }
        return true
      } else {
        throw new Error('Canvas element does not support 2d context. jSignature cannot proceed.')
      }
    }
  }

  jSignatureClass.prototype.initializeCanvas = function (settings) {
    // ===========
    // Init + Sizing code

    var canvas = document.createElement('canvas'),
      $canvas = $(canvas)

    // We cannot work with circular dependency
    if (settings.width === settings.height && settings.height === 'ratio') {
      settings.width = '100%'
    }

    $canvas.css({
      'margin': 0,
      'padding': 0,
      'border': 'none',
      'height': settings.height === 'ratio' || !settings.height ? 1 : settings.height.toString(10),
      'width': settings.width === 'ratio' || !settings.width ? 1 : settings.width.toString(10),
      '-ms-touch-action': 'none',
      'touch-action': 'none',
      'background-color': settings['background-color']
    })

    $canvas.appendTo(this.$parent)

    // we could not do this until canvas is rendered (appended to DOM)
    if (settings.height === 'ratio') {
      $canvas.css('height', Math.round($canvas.width() / settings.sizeRatio))
    } else if (settings.width === 'ratio') {
      $canvas.css('width', Math.round($canvas.height() * settings.sizeRatio))
    }

    $canvas.addClass(apinamespace)

    // canvas's drawing area resolution is independent from canvas's size.
    // pixels are just scaled up or down when internal resolution does not
    // match external size. So...

    canvas.width = $canvas.width()
    canvas.height = $canvas.height()

    // Special case Sizing code

    this.isCanvasEmulator = initializeCanvasEmulator(canvas)

    // End of Sizing Code
    // ===========

    // normally select preventer would be short, but
    // Canvas emulator on IE does NOT provide value for Event. Hence this convoluted line.
    canvas.onselectstart = function (e) {
      if (e && e.preventDefault) {
        e.preventDefault()
      }
      if (e && e.stopPropagation) {
        e.stopPropagation()
      }
      return false
    }

    return canvas
  }

  var GlobalJSignatureObjectInitializer = function (window) {
    var globalEvents = new PubSubClass()

    // common "window resized" event listener.
    // jSignature instances will subscribe to this chanel.
    // to resize themselves when needed.
    ;(function (globalEvents, apinamespace, $, window) {
      'use strict'

      var resizetimer,
        runner = function () {
          globalEvents.publish(apinamespace + '.parentresized')
        }

      // jSignature knows how to resize its content when its parent is resized
      // window resize is the only way we can catch resize events though...
      $(window)
        .bind('resize.' + apinamespace, function () {
          if (resizetimer) {
            clearTimeout(resizetimer)
          }
          resizetimer = setTimeout(runner, 500)
        })
        // when mouse exists canvas element and "up"s outside, we cannot catch it with
        // callbacks attached to canvas. This catches it outside.
        .bind('mouseup.' + apinamespace, function (e) {
          globalEvents.publish(apinamespace + '.windowmouseup')
        })
    })(globalEvents, apinamespace, $, window)

    var jSignatureInstanceExtensions = {
      /*
		'exampleExtension':function(extensionName){
			// we are called very early in instance's life.
			// right after the settings are resolved and
			// jSignatureInstance.events is created
			// and right before first ("jSignature.initializing") event is called.
			// You don't really need to manupilate
			// jSignatureInstance directly, just attach
			// a bunch of events to jSignatureInstance.events
			// (look at the source of jSignatureClass to see when these fire)
			// and your special pieces of code will attach by themselves.

			// this function runs every time a new instance is set up.
			// this means every var you create will live only for one instance
			// unless you attach it to something outside, like "window."
			// and pick it up later from there.

			// when globalEvents' events fire, 'this' is globalEvents object
			// when jSignatureInstance's events fire, 'this' is jSignatureInstance

			// Here,
			// this = is new jSignatureClass's instance.

			// The way you COULD approch setting this up is:
			// if you have multistep set up, attach event to "jSignature.initializing"
			// that attaches other events to be fired further lower the init stream.
			// Or, if you know for sure you rely on only one jSignatureInstance's event,
			// just attach to it directly

			this.events.subscribe(
				// name of the event
				apinamespace + '.initializing'
				// event handlers, can pass args too, but in majority of cases,
				// 'this' which is jSignatureClass object instance pointer is enough to get by.
				, function(){
					if (this.settings.hasOwnProperty('non-existent setting category?')) {
						console.log(extensionName + ' is here')
					}
				}
			)
		}
		*/
    }

    var exportplugins = {
      'default': function (data) {
        return this.toDataURL()
      },
      'native': function (data) {
        return data
      },
      'image': function (data) {
        /*this = canvas elem */
        var imagestring = this.toDataURL()

        if (
          typeof imagestring === 'string' &&
          imagestring.length > 4 &&
          imagestring.slice(0, 5) === 'data:' &&
          imagestring.indexOf(',') !== -1
        ) {
          var splitterpos = imagestring.indexOf(',')

          return [imagestring.slice(5, splitterpos), imagestring.substr(splitterpos + 1)]
        }
        return []
      }
    }

    // will be part of "importplugins"
    function _renderImageOnCanvas(data, formattype, rerendercallable) {
      'use strict'
      // #1. Do NOT rely on this. No worky on IE
      //   (url max len + lack of base64 decoder + possibly other issues)
      // #2. This does NOT affect what is captured as "signature" as far as vector data is
      // concerned. This is treated same as "signature line" - i.e. completely ignored
      // the only time you see imported image data exported is if you export as image.

      // we do NOT call rerendercallable here (unlike in other import plugins)
      // because importing image does absolutely nothing to the underlying vector data storage
      // This could be a way to "import" old signatures stored as images
      // This could also be a way to import extra decor into signature area.

      var img = new Image(),
        // this = Canvas DOM elem. Not jQuery object. Not Canvas's parent div.
        c = this

      img.onload = function () {
        var ctx = c.getContext('2d')
        var oldShadowColor = ctx.shadowColor
        ctx.shadowColor = 'transparent'
        ctx.drawImage(
          img,
          0,
          0,
          img.width < c.width ? img.width : c.width,
          img.height < c.height ? img.height : c.height
        )
        ctx.shadowColor = oldShadowColor
      }

      img.src = 'data:' + formattype + ',' + data
    }

    var importplugins = {
      'native': function (data, formattype, rerendercallable) {
        // we expect data as Array of objects of arrays here - whatever 'default' EXPORT plugin spits out.
        // returning Truthy to indicate we are good, all updated.
        rerendercallable(data)
      },
      'image': _renderImageOnCanvas,
      'image/png;base64': _renderImageOnCanvas,
      'image/jpeg;base64': _renderImageOnCanvas,
      'image/jpg;base64': _renderImageOnCanvas
    }

    function _clearDrawingArea(data, dontClear) {
      this.find('canvas.' + apinamespace)
        .add(this.filter('canvas.' + apinamespace))
        .data(apinamespace + '.this')
        .resetCanvas(data, dontClear)
      return this
    }

    function _setDrawingData(data, formattype) {
      var undef

      if (formattype === undef && typeof data === 'string' && data.substr(0, 5) === 'data:') {
        formattype = data.slice(5).split(',')[0]
        // 5 chars of "data:" + mimetype len + 1 "," char = all skipped.
        data = data.slice(6 + formattype.length)
        if (formattype === data) {
          return
        }
      }

      var $canvas = this.find('canvas.' + apinamespace).add(this.filter('canvas.' + apinamespace))

      if (!importplugins.hasOwnProperty(formattype)) {
        throw new Error(apinamespace + " is unable to find import plugin with for format '" + String(formattype) + "'")
      } else if ($canvas.length !== 0) {
        importplugins[formattype].call(
          $canvas[0],
          data,
          formattype,
          (function (jSignatureInstance) {
            return function () {
              return jSignatureInstance.resetCanvas.apply(jSignatureInstance, arguments)
            }
          })($canvas.data(apinamespace + '.this'))
        )
      }

      return this
    }

    var elementIsOrphan = function (e) {
      var topOfDOM = false
      e = e.parentNode
      while (e && !topOfDOM) {
        topOfDOM = e.body
        e = e.parentNode
      }
      return !topOfDOM
    }

    //These are exposed as methods under $obj.jSignature('methodname', *args)
    var plugins = { 'export': exportplugins, 'import': importplugins, 'instance': jSignatureInstanceExtensions },
      methods = {
        'init': function (options) {
          return this.each(function () {
            if (!elementIsOrphan(this)) {
              new jSignatureClass(this, options, jSignatureInstanceExtensions)
            }
          })
        },
        'destroy': function () {
          return this.each(function () {
            if (!elementIsOrphan(this)) {
              var sig = $(this)
                .find('canvas')
                .data(apinamespace + '.this')
              if (sig) {
                sig.$controlbarLower.remove()
                sig.$controlbarUpper.remove()
                $(sig.canvas).remove()
                for (var e in sig.eventTokens) {
                  if (sig.eventTokens.hasOwnProperty(e)) {
                    globalEvents.unsubscribe(sig.eventTokens[e])
                  }
                }
              }
            }
          })
        },
        'getSettings': function () {
          return this.find('canvas.' + apinamespace)
            .add(this.filter('canvas.' + apinamespace))
            .data(apinamespace + '.this').settings
        },
        'isModified': function () {
          return (
            this.find('canvas.' + apinamespace)
              .add(this.filter('canvas.' + apinamespace))
              .data(apinamespace + '.this').dataEngine._stroke !== null
          )
        },
        'updateSetting': function (param, val, forFuture) {
          var $canvas = this.find('canvas.' + apinamespace)
            .add(this.filter('canvas.' + apinamespace))
            .data(apinamespace + '.this')
          $canvas.settings[param] = val
          $canvas.resetCanvas(forFuture ? null : $canvas.settings.data, true)
          return $canvas.settings[param]
        },
        // around since v1
        'clear': _clearDrawingArea,
        // was mistakenly introduced instead of 'clear' in v2
        'reset': _clearDrawingArea,
        'addPlugin': function (pluginType, pluginName, callable) {
          if (plugins.hasOwnProperty(pluginType)) {
            plugins[pluginType][pluginName] = callable
          }
          return this
        },
        'listPlugins': function (pluginType) {
          var answer = []
          if (plugins.hasOwnProperty(pluginType)) {
            var o = plugins[pluginType]
            for (var k in o) {
              if (o.hasOwnProperty(k)) {
                answer.push(k)
              }
            }
          }
          return answer
        },
        'getData': function (formattype) {
          var undef,
            $canvas = this.find('canvas.' + apinamespace).add(this.filter('canvas.' + apinamespace))
          if (formattype === undef) {
            formattype = 'default'
          }
          if ($canvas.length !== 0 && exportplugins.hasOwnProperty(formattype)) {
            return exportplugins[formattype].call(
              $canvas.get(0), // canvas dom elem
              $canvas.data(apinamespace + '.data'), // raw signature data as array of objects of arrays
              $canvas.data(apinamespace + '.settings')
            )
          }
        },
        // around since v1. Took only one arg - data-url-formatted string with (likely png of) signature image
        'importData': _setDrawingData,
        // was mistakenly introduced instead of 'importData' in v2
        'setData': _setDrawingData,
        // this is one and same instance for all jSignature.
        'globalEvents': function () {
          return globalEvents
        },
        'disable': function () {
          this.find('input').attr('disabled', 1)
          this.find('canvas.' + apinamespace)
            .addClass('disabled')
            .data(apinamespace + '.this').settings.readOnly = true
        },
        'enable': function () {
          this.find('input').removeAttr('disabled')
          this.find('canvas.' + apinamespace)
            .removeClass('disabled')
            .data(apinamespace + '.this').settings.readOnly = false
        },
        // there will be a separate one for each jSignature instance.
        'events': function () {
          return this.find('canvas.' + apinamespace)
            .add(this.filter('canvas.' + apinamespace))
            .data(apinamespace + '.this').events
        }
      } // end of methods declaration.

    $.fn[apinamespace] = function (method) {
      'use strict'
      if (!method || typeof method === 'object') {
        return methods.init.apply(this, arguments)
      } else if (typeof method === 'string' && methods[method]) {
        return methods[method].apply(this, Array.prototype.slice.call(arguments, 1))
      } else {
        $.error('Method ' + String(method) + ' does not exist on jQuery.' + apinamespace)
      }
    }
  } // end of GlobalJSignatureObjectInitializer

  GlobalJSignatureObjectInitializer(window)
})()
